/*
 * Copyright 2017 hv0912
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.vimhe.ezimp2.util;

import com.vimhe.ezimp2.exception.ConversionException;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * 数据类型转换工具类
 *
 * @author Vimhe
 */
public final class ConvertUtils {

    private ConvertUtils() {
    }

    @SuppressWarnings("unchecked")
    public static <T> T convert(String source, Class<T> targetClass) {
        if (source == null) {
            throw new ConversionException("待转换的数据不能为空");
        }
        if (targetClass == null) {
            throw new ConversionException("目标类型不能为空");
        }

        if (String.class == targetClass) {
            return (T) source;
        }

        if (java.util.Date.class == targetClass) {
            try {
                return (T) new SimpleDateFormat("M/d/yy").parse(source);
            } catch (ParseException e) {
                throw new ConversionException("无法转换字符串【" + source + "】为目标类型【" + targetClass.getName() + "】");
            }
        } else if (java.sql.Date.class == targetClass) {
            return (T) java.sql.Date.valueOf(source);
        } else if (java.sql.Timestamp.class == targetClass) {
            return (T) Timestamp.valueOf(source);
        } else if (Byte.class == targetClass) {
            return (T) (isHexNumber(source) ? Byte.decode(source) : Byte.valueOf(source));
        } else if (Short.class == targetClass) {
            return (T) (isHexNumber(source) ? Short.decode(source) : Short.valueOf(source));
        } else if (Integer.class == targetClass) {
            return (T) (isHexNumber(source) ? Integer.decode(source) : Integer.valueOf(source));
        } else if (Long.class == targetClass) {
            return (T) (isHexNumber(source) ? Long.decode(source) : Long.valueOf(source));
        } else if (BigInteger.class == targetClass) {
            return (T) (isHexNumber(source) ? decodeBigInteger(source) : new BigInteger(source));
        } else if (Float.class == targetClass) {
            return (T) Float.valueOf(source);
        } else if (Double.class == targetClass) {
            return (T) Double.valueOf(source);
        } else if (BigDecimal.class == targetClass || Number.class == targetClass) {
            return (T) new BigDecimal(source);
        } else if (Boolean.class == targetClass) {
            return (T) Boolean.valueOf(source);
        } else {
            throw new ConversionException("无法转换字符串【" + source + "】转换为目标类型【" + targetClass.getName() + "】");
        }
    }

    private static boolean isHexNumber(String value) {
        int index = (value.startsWith("-") ? 1 : 0);
        return (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index));
    }

    private static BigInteger decodeBigInteger(String value) {
        int radix = 10;
        int index = 0;
        boolean negative = false;

        // Handle minus sign, if present.
        if (value.startsWith("-")) {
            negative = true;
            index++;
        }

        // Handle radix specifier, if present.
        if (value.startsWith("0x", index) || value.startsWith("0X", index)) {
            index += 2;
            radix = 16;
        } else if (value.startsWith("#", index)) {
            index++;
            radix = 16;
        } else if (value.startsWith("0", index) && value.length() > 1 + index) {
            index++;
            radix = 8;
        }

        BigInteger result = new BigInteger(value.substring(index), radix);
        return (negative ? result.negate() : result);
    }

}
